#include <tsinide/MainWindow.hpp>
#include <tsinide/TsinConfigDialog.hpp>
#include <ui_MainWindow.h>

#include <QtGui>
#include <QCloseEvent>
#include <QProcess>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow),
        curTsin(0)
{
        ui->setupUi(this);
        ui->consoleOutputDock->setVisible(false);

        readSettings();

        connect(ui->codeEditor->document(), SIGNAL(contentsChanged()),
                this, SLOT(documentModified()));

        /* A workaround of QTBUG 16507 */
        QTimer::singleShot(0, this, SLOT(readSettings()));
}

MainWindow::~MainWindow()
{
        delete ui;
}

void MainWindow::closeEvent(QCloseEvent *event)
{
        if (saveBeforeExit()) {
                writeSettings();
                event->accept();
        } else
                event->ignore();
}

void MainWindow::makeScratch()
{
        ui->codeEditor->clear();
        curFilename = tr("<scratch>");
        setWindowFilePath(curFilename);
        setWindowModified(false);
}

void MainWindow::readSettings()
{
        QSettings settings;
        curFilename = settings.value("system/curFile", tr("<scratch>")).toString();

        if (curFilename != "<scratch>" && QFile(curFilename).exists())
                loadFile(curFilename);
        else
                makeScratch();
}

void MainWindow::writeSettings()
{
        QSettings settings;
        settings.setValue("system/curFile", curFilename);
}

void MainWindow::newFile()
{
        if (!closeFile())
                return;
        makeScratch();
}

void MainWindow::openFile()
{
        if (!saveBeforeExit())
                return;

        QString filename = QFileDialog::getOpenFileName(this, tr("Open File"),
                                                        curFilename == "<scratch>" ? QString() : curFilename,
                                                        tr("SCDL scripts (*.scdl)"));
        if (filename.isEmpty())
                return;

        loadFile(filename);
}

bool MainWindow::saveFile()
{
        if (!ui->codeEditor->document()->isModified() && curFilename != "<scratch>")
                return true;

        if (curFilename == "<scratch>")
                return saveFileAs();
        else
                return saveFile(curFilename);
}

bool MainWindow::loadFile(const QString &filename)
{
        QFile file(filename);
        if (!file.open(QFile::ReadOnly | QFile::Text)) {
                QMessageBox::warning(this, tr("Open File"),
                                     tr("Cannot open %1: %2\n").arg(filename).arg(file.errorString()));
                return false;
        }

        QTextStream in(&file);
        ui->codeEditor->setPlainText(in.readAll());
        curFilename = QFileInfo(filename).canonicalFilePath();
        setWindowFilePath(curFilename);
        setWindowModified(false);
        return true;
}

bool MainWindow::saveFile(const QString &filename)
{
        QFile file(filename);
        if (!file.open(QFile::WriteOnly | QFile::Text)) {
                QMessageBox::warning(this, tr("Save File"),
                                     tr("Cannot save to %1: %2\n").arg(filename).arg(file.errorString()));
                return false;
        }

        QTextStream out(&file);
        out << ui->codeEditor->toPlainText();
        curFilename = QFileInfo(filename).canonicalFilePath();
        ui->codeEditor->document()->setModified(false);
        setWindowModified(false);
        return true;
}

bool MainWindow::saveFileAs()
{
        QString filename = QFileDialog::getSaveFileName(this, tr("Save As"),
                                                        curFilename,
                                                        tr("SCDL scripts (*.scdl)"));
        if (!filename.isEmpty())
                return saveFile(filename);
        return false;
}

bool MainWindow::saveBeforeExit()
{
        bool ret = true;

        if (ui->codeEditor->document()->isModified()) {
                QMessageBox box;
                box.setWindowTitle(tr("tsinIDE"));
                box.setIcon(QMessageBox::Warning);
                box.setText(tr("Save ") + curFilename + tr(" before closing?"));
                box.setStandardButtons(QMessageBox::Yes | QMessageBox::No |
                                       QMessageBox::Cancel);
                switch (box.exec()) {
                case QMessageBox::Yes:
                        ret = saveFile();
                        break;
                case QMessageBox::No:
                        break;
                case QMessageBox::Cancel:
                        ret = false;
                        break;
                }
        }

        return ret;
}

bool MainWindow::closeFile()
{
        if (!saveBeforeExit())
                return false;

        makeScratch();
        return true;
}

void MainWindow::on_action_New_triggered()
{
        newFile();
}

void MainWindow::on_action_Open_triggered()
{
        openFile();
}

void MainWindow::on_action_Save_triggered()
{
        saveFile();
}

void MainWindow::on_action_SaveAs_triggered()
{
        saveFileAs();
}

void MainWindow::on_action_Close_triggered()
{
        closeFile();
}

void MainWindow::on_action_Undo_triggered()
{
        ui->codeEditor->undo();
}

void MainWindow::on_action_Redo_triggered()
{
        ui->codeEditor->redo();
}

void MainWindow::on_action_Cut_triggered()
{
        ui->codeEditor->cut();
}

void MainWindow::on_action_Copy_triggered()
{
        ui->codeEditor->copy();
}

void MainWindow::on_action_Paste_triggered()
{
        ui->codeEditor->paste();
}

void MainWindow::on_action_Run_triggered()
{
        QSettings settings;
        QString path = settings.value("tsin/path", "tsin.exe").toString();
        int loglvl = settings.value("tsin/loglvl", 4).toInt();
        QString extra = settings.value("tsin/cmdline", "").toString();

        QStringList cmdline;
        cmdline << "-f" << curFilename;
        cmdline << "--loglvl" << QString::number(loglvl);
        cmdline << extra;

        curTsin = new QProcess(this);
        curTsin->setReadChannel(QProcess::StandardError);

        QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
        curTsin->setProcessEnvironment(env);

        curTsin->start(path, cmdline);
        connect(curTsin, SIGNAL(finished(int, QProcess::ExitStatus)),
                this, SLOT(onTsinFinished(int, QProcess::ExitStatus)));
        curTsin->waitForStarted();

        connect(curTsin, SIGNAL(readyRead()),
                this, SLOT(readTsinOutput()));
        ui->consoleOutputDock->setVisible(true);
        ui->consoleOutput->clear();
}

void MainWindow::on_action_Options_triggered()
{
        TsinConfigDialog dlg;
        dlg.exec();
}

void MainWindow::readTsinOutput()
{
        QProcess *pr = static_cast<QProcess *>(sender());
        char buf[256];
        qint64 end;
        while (pr->canReadLine()) {
                end = pr->readLine(buf, sizeof(buf)) - 1;
                if (end <= 0)
                        break;
                while (buf[end] == '\n' || buf[end] == '\r')
                        buf[end --] = '\0';
                ui->consoleOutput->appendPlainText(buf);
        }
}

void MainWindow::onTsinFinished(int code, QProcess::ExitStatus status)
{
        ui->consoleOutput->appendPlainText("");
        ui->consoleOutput->appendPlainText(tr("=== process exited with code %1 ===").arg(code));

        delete curTsin;
        curTsin = 0;
}

void MainWindow::documentModified()
{
        setWindowModified(ui->codeEditor->document()->isModified());
}
